#pragma once
#include "charset/ascii.hpp"
#include "stdlib/bugcheck.hpp"
using namespace JHCPP::stdlib;

namespace JHCPP
{
	namespace charset
	{	
		//
		//trim
		//
		template <class T>
		T trimLeft(const T& str)
		{
			typename T::const_iterator it  = str.begin();
			typename T::const_iterator end = str.end();

			while (it != end && CAscii::isSpace(*it)) ++it;
			return T(it, end);
		}

		template <class T>
		T& trimLeftInPlace(T& str)
		{
			typename T::iterator it  = str.begin();
			typename T::iterator end = str.end();

			while (it != end && CAscii::isSpace(*it)) ++it;
			str.erase(str.begin(), it);
			return str;
		}

		template <class T>
		T trimRight(const T& str)
		{
			int pos = int(str.size()) - 1;

			while (pos >= 0 && CAscii::isSpace(str[pos])) --pos;
			return T(str, 0, pos + 1);
		}

		template <class T>
		T& trimRightInPlace(T& str)
		{
			int pos = int(str.size()) - 1;

			while (pos >= 0 && CAscii::isSpace(str[pos])) --pos;
			str.resize(pos + 1);

			return str;
		}

		template <class T>
		T trim(const T& str)
		{
			int first = 0;
			int last  = int(str.size()) - 1;

			while (first <= last && CAscii::isSpace(str[first])) ++first;
			while (last >= first && CAscii::isSpace(str[last])) --last;

			return T(str, first, last - first + 1);
		}

		template <class T>
		T& trimInPlace(T& str)
		{
			int first = 0;
			int last  = int(str.size()) - 1;

			while (first <= last && CAscii::isSpace(str[first])) ++first;
			while (last >= first && CAscii::isSpace(str[last])) --last;

			str.resize(last + 1);
			str.erase(0, first);

			return str;
		}

		//
		//Upper
		//
		template <class T>
		T toUpper(const T& str)
		{
			typename T::const_iterator it  = str.begin();
			typename T::const_iterator end = str.end();

			T result;
			result.reserve(str.size());
			while (it != end) result += CAscii::toUpper(*it++);
			return result;
		}

		template <class T>
		T& toUpperInPlace(T& str)
			/// Replaces all characters in str with their upper-case counterparts.
		{
			typename T::iterator it  = str.begin();
			typename T::iterator end = str.end();

			while (it != end) { *it = CAscii::toUpper(*it); ++it; }
			return str;
		}

		//
		//Lower
		//
		template <class T>
		T toLower(const T& str)
		{
			typename T::const_iterator it  = str.begin();
			typename T::const_iterator end = str.end();

			T result;
			result.reserve(str.size());
			while (it != end) result += CAscii::toLower(*it++);
			return result;
		}


		template <class T>
		T& toLowerInPlace(T& str)
			/// Replaces all characters in str with their lower-case counterparts.
		{
			typename T::iterator it  = str.begin();
			typename T::iterator end = str.end();

			while (it != end) { *it = CAscii::toLower(*it); ++it; }
			return str;
		}

		//
		//icompare
		//
		template <class T, class It>
		int icompare(const T& str, typename T::size_type pos, typename T::size_type n, It it2, It end2)
		{
			typename T::size_type sz = str.size();
			if (pos > sz) pos = sz;
			if (pos + n > sz) n = sz - pos;
			It it1  = str.begin() + pos; 
			It end1 = str.begin() + pos + n;
			while (it1 != end1 && it2 != end2)
			{
				typename T::value_type c1(CAscii::toLower(*it1));
				typename T::value_type c2(CAscii::toLower(*it2));
				if (c1 < c2)
					return -1;
				else if (c1 > c2)
					return 1;
				++it1; ++it2;
			}

			if (it1 == end1)
				return it2 == end2 ? 0 : -1;
			else
				return 1;
		}

		template <class T>
		int icompare(const T& str1, const T& str2)
		{
			typename T::const_iterator it1(str1.begin());
			typename T::const_iterator end1(str1.end());
			typename T::const_iterator it2(str2.begin());
			typename T::const_iterator end2(str2.end());
			while (it1 != end1 && it2 != end2)
			{
				typename T::value_type c1(CAscii::toLower(*it1));
				typename T::value_type c2(CAscii::toLower(*it2));
				if (c1 < c2)
					return -1;
				else if (c1 > c2)
					return 1;
				++it1; ++it2;
			}

			if (it1 == end1)
				return it2 == end2 ? 0 : -1;
			else
				return 1;
		}

		template <class T>
		int icompare(const T& str1, typename T::size_type n1, const T& str2, typename T::size_type n2)
		{
			if (n2 > str2.size()) n2 = str2.size();
			return icompare(str1, 0, n1, str2.begin(), str2.begin() + n2);
		}

		template <class T>
		int icompare(const T& str1, typename T::size_type n, const T& str2)
		{
			if (n > str2.size()) n = str2.size();
			return icompare(str1, 0, n, str2.begin(), str2.begin() + n);
		}

		template <class T>
		int icompare(const T& str1, typename T::size_type pos, typename T::size_type n, const T& str2)
		{
			return icompare(str1, pos, n, str2.begin(), str2.end());
		}

		template <class T>
		int icompare(
			const T& str1, typename T::size_type pos1, typename T::size_type n1, 
			const T& str2, typename T::size_type pos2, typename T::size_type n2)
		{
			typename T::size_type sz2 = str2.size();
			if (pos2 > sz2) pos2 = sz2;
			if (pos2 + n2 > sz2) n2 = sz2 - pos2;
			return icompare(str1, pos1, n1, str2.begin() + pos2, str2.begin() + pos2 + n2);
		}

		template <class T>
		int icompare(
			const T& str1, typename T::size_type pos1, typename T::size_type n, 
			const T& str2, typename T::size_type pos2)
		{
			typename T::size_type sz2 = str2.size();
			if (pos2 > sz2) pos2 = sz2;
			if (pos2 + n > sz2) n = sz2 - pos2;
			return icompare(str1, pos1, n, str2.begin() + pos2, str2.begin() + pos2 + n);
		}

		template <class T>
		int icompare(const T& str, typename T::size_type pos, typename T::size_type n, const typename T::value_type* ptr)
		{
			jh_check_ptr (ptr);
			typename T::size_type sz = str.size();
			if (pos > sz) pos = sz;
			if (pos + n > sz) n = sz - pos;
			typename T::const_iterator it  = str.begin() + pos; 
			typename T::const_iterator end = str.begin() + pos + n;
			while (it != end && *ptr)
			{
				typename T::value_type c1(CAscii::toLower(*it));
				typename T::value_type c2(CAscii::toLower(*ptr));
				if (c1 < c2)
					return -1;
				else if (c1 > c2)
					return 1;
				++it; ++ptr;
			}

			if (it == end)
				return *ptr == 0 ? 0 : -1;
			else
				return 1;
		}

		template <class T>
		int icompare(const T& str, typename T::size_type pos, const typename T::value_type* ptr)
		{
			return icompare(str, pos, str.size() - pos, ptr);
		}

		template <class T>
		int icompare(const T& str, const typename T::value_type* ptr)
		{
			return icompare(str, 0, str.size(), ptr);
		}


		//
		//translate
		//
		template <class T>
		T translate(const T& str, const T& from, const T& to)
		{
			T result;
			result.reserve(str.size());
			typename T::const_iterator it  = str.begin();
			typename T::const_iterator end = str.end();
			typename T::size_type toSize = to.size();
			while (it != end)
			{
				typename T::size_type pos = from.find(*it);
				if (pos == T::npos)
				{
					result += *it;
				}
				else
				{
					if (pos < toSize) result += to[pos];
				}
				++it;
			}
			return result;
		}

		template <class T>
		T translate(const T& str, const typename T::value_type* from, const typename T::value_type* to)
		{
			jh_check_ptr (from);
			jh_check_ptr (to);
			return translate(str, T(from), T(to));
		}

		template <class T>
		T& translateInPlace(T& str, const T& from, const T& to)
		{
			str = translate(str, from, to);
			return str;
		}

		template <class T>
		T translateInPlace(T& str, const typename T::value_type* from, const typename T::value_type* to)
		{
			jh_check_ptr (from);
			jh_check_ptr (to);
			str = translate(str, T(from), T(to));
			return str;
		}


		//
		//replace
		//
		template <class T>
		T& replaceInPlace(T& str, const T& from, const T& to, typename T::size_type start = 0)
		{
			jh_assert (from.size() > 0);

			T result;
			typename T::size_type pos = 0;
			result.append(str, 0, start);
			do
			{
				pos = str.find(from, start);
				if (pos != T::npos)
				{
					result.append(str, start, pos - start);
					result.append(to);
					start = pos + from.length();
				}
				else result.append(str, start, str.size() - start);
			}
			while (pos != T::npos);
			str.swap(result);
			return str;
		}

		template <class T>
		T& replaceInPlace(T& str, const typename T::value_type* from, const typename T::value_type* to, typename T::size_type start = 0)
		{
			jh_assert (*from);

			T result;
			typename T::size_type pos = 0;
			typename T::size_type fromLen = std::strlen(from);
			result.append(str, 0, start);
			do
			{
				pos = str.find(from, start);
				if (pos != T::npos)
				{
					result.append(str, start, pos - start);
					result.append(to);
					start = pos + fromLen;
				}
				else result.append(str, start, str.size() - start);
			}
			while (pos != T::npos);
			str.swap(result);
			return str;
		}

		template <class T>
		T replace(const T& str, const T& from, const T& to, typename T::size_type start = 0)
		{
			T result(str);
			replaceInPlace(result, from, to, start);
			return result;
		}

		template <class T>
		T replace(const T& str, const typename T::value_type* from, const typename T::value_type* to, typename T::size_type start = 0)
		{
			T result(str);
			replaceInPlace(result, from, to, start);
			return result;
		}


		//
		//cat
		//
		template <class T>
		T cat(const T& s1, const T& s2)
		{
			S result = s1;
			result.reserve(s1.size() + s2.size());
			result.append(s2);
			return result;
		}

		template <class T>
		T cat(const T& s1, const T& s2, const T& s3)
		{
			T result = s1;
			result.reserve(s1.size() + s2.size() + s3.size());
			result.append(s2);
			result.append(s3);
			return result;
		}

		template <class T>
		T cat(const T& s1, const T& s2, const T& s3, const T& s4)
		{
			T result = s1;
			result.reserve(s1.size() + s2.size() + s3.size() + s4.size());
			result.append(s2);
			result.append(s3);
			result.append(s4);
			return result;
		}

		template <class T>
		T cat(const T& s1, const T& s2, const T& s3, const T& s4, const T& s5)
		{
			T result = s1;
			result.reserve(s1.size() + s2.size() + s3.size() + s4.size() + s5.size());
			result.append(s2);
			result.append(s3);
			result.append(s4);
			result.append(s5);
			return result;
		}

		template <class T>
		T cat(const T& s1, const T& s2, const T& s3, const T& s4, const T& s5, const T& s6)
		{
			T result = s1;
			result.reserve(s1.size() + s2.size() + s3.size() + s4.size() + s5.size() + s6.size());
			result.append(s2);
			result.append(s3);
			result.append(s4);
			result.append(s5);
			result.append(s6);
			return result;
		}

		template <class T, class It>
		T cat(const T& delim, const It& begin, const It& end)
		{
			T result;
			for (It it = begin; it != end; ++it)
			{
				if (!result.empty()) result.append(delim);
				result += *it;
			}
			return result;
		}
	}//end of namespace charset
}//end of namespace JHCPP
